﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Odelle.Utils.Comparisons;

namespace Odelle.Utils.Comparisons
{
    public class XmlQuery
    {
        #region Properties

        private FlexibleStringComparator FlexComparer = new FlexibleStringComparator();

        public FlexibleStringComparator.QueryOperator DefaultOperator
        {
            set { FlexComparer.DefaultOperator = value; }
            get { return FlexComparer.DefaultOperator; }
        }

        #endregion



        //
        public string[] GetAttributeValues(XmlElement element, string attributeName)
        {
            List<string> list = new List<string>();
            list.Clear();

            _getAttValues(element, attributeName, ref list);
            return list.ToArray();
        }
        private void _getAttValues(XmlElement element, string attributeName, ref List<String> list)
        {
            if (element.HasAttribute(attributeName))
            {
                string attr = element.GetAttribute(attributeName);

                if (!list.Contains(attr))
                {
                    list.Add(attr);
                }
            }

            if (element.HasChildNodes)
            {
                foreach (XmlNode node in element.ChildNodes)
                {
                    if (node.NodeType == XmlNodeType.Element)
                    {
                        _getAttValues((XmlElement)node, attributeName, ref list);
                    }
                }
            }
        }

        public string[] GetAttributeNames(XmlElement element)
        {
            List<string> list = new List<string>();
            list.Clear();

            _getAttNames(element, ref list);
            return list.ToArray();
        }
        private void _getAttNames(XmlElement element, ref List<String> list)
        {
            if (element == null)
                return;

            if (element.HasAttributes)
            {
                foreach (XmlAttribute attr in element.Attributes)
                {
                    if (!list.Contains(attr.Name))
                    {
                        list.Add(attr.Name);
                    }
                }
            }

            if (element.HasChildNodes)
            {
                foreach (XmlNode node in element.ChildNodes)
                {
                    if (node.NodeType == XmlNodeType.Element)
                    {
                        _getAttNames((XmlElement)node, ref list);
                    }
                }
            }
        }

        #region Query XML Single Element
        //
        public XmlElement QueryXmlSingleElement(XmlElement element, string[] attributePairs)
        {
            return QueryXmlSingleElement(element, attributePairs, this.DefaultOperator);
        }

        public XmlElement QueryXmlSingleElement(XmlElement element, string[] attributePairs, FlexibleStringComparator.QueryOperator queryOperator)
        {
            if (element == null || attributePairs.Length == 0 )
                return null;

            string[] names = new string[attributePairs.Length];
            string[] values = new string[attributePairs.Length];

            for (int i = 0; i < attributePairs.Length; ++i)
            {
                names[i] = "";
                values[i] = "";

                if (attributePairs[i].Contains('\\'))
                {
                    string[] pair = attributePairs[i].Split('\\');

                    names[i] = pair[0];
                    values[i] = attributePairs[i].Substring(pair[0].Length + 1);
                }
                else
                {
                    names[i] = attributePairs[i];
                    values[i] = "";
                }
            }

            return QueryXmlSingleElement(element, names, values, queryOperator);
        }

        public XmlElement QueryXmlSingleElement(XmlElement element, string[] attributeNames, string[] attributeValues)
        {
            return QueryXmlSingleElement(element, attributeNames, attributeValues, this.DefaultOperator);
        }

        public XmlElement QueryXmlSingleElement(XmlElement element, string[] attributeNames, string[] attributeValues, FlexibleStringComparator.QueryOperator queryOperator)
        {
            if (element == null || attributeNames == null || attributeValues == null )
            {
                return null;
            }

            int paramLen = Math.Min(attributeNames.Length, attributeValues.Length);

            if (paramLen == 0)
            {
                return null;
            }

            for (int i = 0; i < paramLen; ++i)
            {
                if (element != null)
                {
                    element = QueryXmlSingleElement(element, attributeNames[i], attributeValues[i], queryOperator);
                }
            }

            return element;
        }

        public XmlElement QueryXmlSingleElement(XmlElement element, string attributeName, string attributeValue)
        {
            return QueryXmlSingleElement(element, attributeName, attributeValue, this.DefaultOperator);
        }

        public XmlElement QueryXmlSingleElement(XmlElement element, string attributeName, string attributeValue, FlexibleStringComparator.QueryOperator queryOperator)
        {
            if (element == null)
            {
                return null;
            }

            if (attributeName == "")
            {
                return null;
            }

            if (element.HasAttribute(attributeName))
            {
                string attribute = element.GetAttribute(attributeName);

                if (FlexComparer.Compare(attribute, attributeValue, queryOperator))
                {
                    return element;
                }
            }

            foreach (XmlNode node in element.ChildNodes)
            {
                if (node.NodeType == XmlNodeType.Element)
                {
                    XmlElement retEle = QueryXmlSingleElement((XmlElement)node, attributeName, attributeValue, queryOperator);
                    if (retEle != null)
                    {
                        return retEle;
                    }
                }
            }

            return null;
        }

        #endregion

        #region Query Xml Elements

        public XmlElement[] QueryXmlElements(XmlElement[] elements, string[] attributePairs)
        {
            return QueryXmlElements(elements, attributePairs, this.DefaultOperator);
        }

        public XmlElement[] QueryXmlElements(XmlElement[] elements, string[] attributePairs, FlexibleStringComparator.QueryOperator queryOperator)
        {
            if (elements == null || attributePairs.Length == 0)
                return null;

            string[] names = new string[attributePairs.Length];
            string[] values = new string[attributePairs.Length];

            for (int i = 0; i < attributePairs.Length; ++i)
            {
                names[i] = "";
                values[i] = "";

                if (attributePairs[i].Contains('\\'))
                {
                    string[] pair = attributePairs[i].Split('\\');

                    names[i] = pair[0];
                    values[i] = attributePairs[i].Substring(pair[0].Length + 1);
                }
                else
                {
                    names[i] = attributePairs[i];
                    values[i] = "";
                }
            }

            return QueryXmlElements(elements, names, values, queryOperator);
        }

        public XmlElement[] QueryXmlElements(XmlElement[] elements, string[] attributeNames, string[] attributeValues)
        {
            return QueryXmlElements(elements, attributeNames, attributeValues, this.DefaultOperator);
        }

        public XmlElement[] QueryXmlElements(XmlElement[] elements, string[] attributeNames, string[] attributeValues, FlexibleStringComparator.QueryOperator queryOperator)
        {
            _elementList.Clear();
            if (elements == null || attributeNames == null || attributeValues == null)
            {
                return _elementList.ToArray();
            }

            int paramLen = Math.Min(attributeNames.Length, attributeValues.Length);

            if (paramLen == 0)
            {
                return _elementList.ToArray();
            }

            XmlElement[] ele = elements;

            for (int i = 0; i < paramLen; ++i)
            {
                _elementList.Clear();

                for (int j = 0; j < ele.Length; ++j)
                {
                    if (ele[j] != null)
                    {
                        _queryXmlElements(ele[j], attributeNames[i], attributeValues[i], queryOperator);
                    }
                }

                ele = _elementList.ToArray();
            }

            return ele;
        }

        public XmlElement[] QueryXmlElements(XmlElement[] elements, string attributeName, string attributeValue)
        {
            _elementList.Clear();
            return QueryXmlElements(elements, attributeName, attributeValue, this.DefaultOperator);
        }

        public XmlElement[] QueryXmlElements(XmlElement[] elements, string attributeName, string attributeValue, FlexibleStringComparator.QueryOperator queryOperator)
        {
            _elementList.Clear();

            for (int i = 0; i < elements.Length; ++i)
            {
                _queryXmlElements(elements[i], attributeName, attributeName, queryOperator);
            }

            return _elementList.ToArray();
        }

        private List<XmlElement> _elementList = new List<XmlElement>();
        private void _queryXmlElements(XmlElement element, string attributeName, string attributeValue, FlexibleStringComparator.QueryOperator queryOperator)
        {
            if (element == null)
            {
                return;
            }

            if (attributeName == "")
            {
                return;
            }

            if (element.HasAttribute(attributeName))
            {
                string attribute = element.GetAttribute(attributeName);

                if (FlexComparer.Compare(attribute, attributeValue, queryOperator))
                {
                    _elementList.Add( element);
                }
            }

            foreach (XmlNode node in element.ChildNodes)
            {
                if (node.NodeType == XmlNodeType.Element)
                {
                    _queryXmlElements((XmlElement)node, attributeName, attributeValue, queryOperator);
                }
            }

            return;
        }
        
        #endregion
    }
}
